﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.IO;
using Newtonsoft.Json;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Text.RegularExpressions;
using System.Collections;
using System.Web.UI;
using System.IO.Compression;
using System.Web;
using System.Web.Mvc;

namespace OpenQuarters.WebQuarters.Utils
{
    public static class GeneralUtils
    {
        /// Static Method to render string - put somewhere of your choosing
        public static string RenderPartialToString(string controlName, object viewData)
        {
            ViewDataDictionary vd = new ViewDataDictionary(viewData);
            ViewPage vp = new ViewPage { ViewData = vd };
            Control control = vp.LoadControl(controlName);

            vp.Controls.Add(control);

            StringBuilder sb = new StringBuilder();
            using (StringWriter sw = new StringWriter(sb))
            {
                using (HtmlTextWriter tw = new HtmlTextWriter(sw))
                {
                    vp.RenderControl(tw);
                }
            }

            return sb.ToString();
        }

        ///// <summary>Renders a view to string.</summary> 
        //public static string RenderPartialToString(this HtmlHelper html, string viewName, object viewData)
        //{
        //    return RenderViewToString(html.ViewContext.Controller.ControllerContext, viewName, viewData);
        //}
        ///// <summary>Renders a view to string.</summary> 
        //public static string RenderViewToString(this Controller controller, string viewName, object viewData)
        //{
        //    return RenderViewToString(controller.ControllerContext, viewName, viewData);
        //}

        //private static string RenderViewToString(ControllerContext context,
        //                                        string viewName, object viewData)
        //{
        //    //Create memory writer 
        //    var sb = new StringBuilder();
        //    var memWriter = new StringWriter(sb);

        //    //Create fake http context to render the view 
        //    var fakeResponse = new HttpResponse(memWriter);
        //    var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
        //    var fakeControllerContext = new ControllerContext(
        //        new HttpContextWrapper(fakeContext),
        //        context.RouteData, context.Controller);

        //    var oldContext = HttpContext.Current;
        //    HttpContext.Current = fakeContext;

        //    //Use HtmlHelper to render partial view to fake context 
        //    var html = new HtmlHelper(new ViewContext(fakeControllerContext,
        //        new FakeView(), new ViewDataDictionary(), new TempDataDictionary()),
        //        new ViewPage());
        //    html.RenderPartial(viewName, viewData);

        //    //Restore context 
        //    HttpContext.Current = oldContext;

        //    //Flush memory and return output 
        //    memWriter.Flush();
        //    return sb.ToString();
        //}

        ///// <summary>Fake IView implementation, only used to instantiate an HtmlHelper.</summary> 
        //public class FakeView : IView
        //{
        //    #region IView Members
        //    public void Render(ViewContext viewContext, System.IO.TextWriter writer)
        //    {
        //        throw new NotImplementedException();
        //    }
        //    #endregion
        //}


        public static string RenderViewToString(
          ControllerContext controllerContext,
          string viewPath,
          string masterPath,
          ViewDataDictionary viewData,
          TempDataDictionary tempData)
        {
            Stream filter = null;
            ViewPage viewPage = new ViewPage();

            //Right, create our view
            viewPage.ViewContext = new ViewContext(controllerContext, new WebFormView(viewPath, masterPath), viewData, tempData);

            //Get the response context, flush it and get the response filter.
            var response = viewPage.ViewContext.HttpContext.Response;
            response.Flush();
            var oldFilter = response.Filter;

            try
            {
                //Put a new filter into the response
                filter = new MemoryStream();
                response.Filter = filter;

                //Now render the view into the memorystream and flush the response
                viewPage.ViewContext.View.Render(viewPage.ViewContext, viewPage.ViewContext.HttpContext.Response.Output);
                response.Flush();

                //Now read the rendered view.
                filter.Position = 0;
                var reader = new StreamReader(filter, response.ContentEncoding);
                return reader.ReadToEnd();
            }
            finally
            {
                //Clean up.
                if (filter != null)
                {
                    filter.Dispose();
                }

                //Now replace the response filter
                response.Filter = oldFilter;
            }
        }

        public static void GZipIfSupported()
        {
            if (IsGZipSupported())
            {
                if (HttpContext.Current.Request.Headers["Accept-Encoding"].Contains("gzip"))
                {
                    HttpContext.Current.Response.Filter = new GZipStream(HttpContext.Current.Response.Filter, CompressionMode.Compress);
                    HttpContext.Current.Response.AppendHeader("Content-Encoding", "gzip");
                }
                else
                {
                    HttpContext.Current.Response.Filter = new DeflateStream(HttpContext.Current.Response.Filter, CompressionMode.Compress);
                    HttpContext.Current.Response.AppendHeader("Content-Encoding", "deflate");
                }
            }
        }

        /// <summary>
        /// Determines if GZip is supported
        /// </summary>
        /// <returns></returns>
        public static bool IsGZipSupported()
        {
            string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
            if (!string.IsNullOrEmpty(AcceptEncoding) && (AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))
            {
                return true;
            }
            return false;
        }

        public static string ToCamelCase(string input)
        {
            return input.Substring(0, 1).ToLower() + input.Replace(" ", "").Substring(1);
        }

        public static int ToInt32(this object obj)
        {
            return Convert.ToInt32(obj);
        }

        public static long ToInt64(this object obj)
        {
            return Convert.ToInt64(obj);
        }

        public static string StripHTML(this string inputString)
        {
            return Regex.Replace
              (inputString, "<.*?>", string.Empty);
        }

        public static string UrlEncode(this string inputString)
        {
            return HttpUtility.UrlEncode(inputString);
        }

        public static string UrlDecode(this string inputString)
        {
            return HttpUtility.UrlDecode(inputString);
        }

        public static List<Type> GetSubclassesOf(Type type, bool ignoreSystem)
        {
            List<Type> lReturn = new List<Type>();
            foreach (var a in System.Threading.Thread.GetDomain().GetAssemblies())
            {
                if (ignoreSystem && a.FullName.StartsWith("System."))
                {
                    continue;
                }
                try
                {
                    foreach (var t in a.GetTypes())
                    {
                        if (t.IsSubclassOf(type) || (type.IsInterface && t.GetInterfaces().FirstOrDefault(e => e.FullName == type.FullName) != null))
                        {
                            lReturn.Add(t);
                        }
                    }
                }
                catch (System.Reflection.ReflectionTypeLoadException ex)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (Exception typeEx in ex.LoaderExceptions)
                    {
                        sb.Append(typeEx.Message + "; ");
                    }
                    throw new ApplicationException("Unable to find type(s): " + sb.ToString());
                }
            }
            return lReturn;
        }

        public static List<MethodInfo> GetMethodsWithAttribute<AttributeType, ContainerAttributeType>()
        {
            List<MethodInfo> list = new List<MethodInfo>();
            var assemblies = Thread.GetDomain().GetAssemblies().Where(a => !a.FullName.StartsWith("System."));
            foreach (var assembly in assemblies)
            {
                try
                {
                    var types = assembly.GetTypes().Where(t => t.GetCustomAttributes(typeof(ContainerAttributeType), true).ToList().Count > 0);
                    foreach (var type in types)
                    {
                        var methods = type.GetMethods().Where(m => m.GetCustomAttributes(typeof(AttributeType), true).ToList().Count > 0).ToList();
                        list.AddRange(methods);
                    }
                }
                catch
                {
                }
            }
            return list;
        }


        public static string MD5Encrypt(this string Plaintext)
        {
            System.Text.UTF8Encoding ue = new System.Text.UTF8Encoding();
            byte[] bytes = ue.GetBytes(Plaintext);

            // encrypt bytes
            System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] hashBytes = md5.ComputeHash(bytes);

            // Convert the encrypted bytes back to a string (base 16)
            string hashString = "";

            for (int i = 0; i < hashBytes.Length; i++)
            {
                hashString += Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
            }

            return hashString.PadLeft(32, '0');
        }

        /// <summary>
        /// Returns a Type given a fully-qualified Type name.
        /// If the Type is not found in the currently-loaded assemblies, null is returned.
        /// </summary>
        /// <param name="fullName">The fully-qualified Type name being searched for</param>
        /// <returns></returns>
        public static Type GetTypeByName(string fullName)
        {
            if (fullName.Length > 0)
            {
                if (HttpContext.Current.Application["__Type_" + fullName.ToLowerInvariant()] != null)
                {
                    return (Type)HttpContext.Current.Application["__Type_" + fullName.ToLowerInvariant()];
                }
                foreach (Assembly a in Thread.GetDomain().GetAssemblies())
                {
                    Type t = a.GetType(fullName);
                    if (t != null)
                    {
                        HttpContext.Current.Application["__Type_" + fullName.ToLowerInvariant()] = t;
                        return t;
                    }
                }
            }
            return null;
        }

        public static T GetNewObject<T>()
        {
            try
            {
                return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
            }
            catch
            {
                return default(T);
            }
        }

        public static object GetNewObject(this Type t)
        {
            try
            {
                return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
            }
            catch
            {
                return null;
            }
        }

        public static object CallMethod(string id)
        {
            string[] args = id.Split(',');

            if (args.Length < 3)
                throw new ArgumentException("The assembly name, module name and method must be passed to the handler seperated by a comma");

            string assemblyName = args[0];
            string moduleName = args[1];
            string method = args[2];

            Assembly assembly = Assembly.Load(assemblyName);
            Type moduleType = GeneralUtils.GetTypeByName(assemblyName + "." + moduleName);

            MethodInfo methodInfo = moduleType.GetMethod(method, new Type[] { });

            if (methodInfo == null)
            {
                throw new ArgumentException("Specified method not found");
            }

            return methodInfo.Invoke(assembly, new object[] { });
        }

        public static string DescribeException(this Exception ex)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Message: " + ex.Message);
            sb.AppendLine("Source: " + ex.Source);
            sb.AppendLine("StackTrace: " + ex.StackTrace);
            if (ex.InnerException != null)
            {
                sb.AppendLine("InnerException:");
                sb.AppendLine(ex.InnerException.DescribeException());
            }
            return sb.ToString();
        }

        public static string ToUrlSafeString(this string targetString)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(GeneralUtils.KeepOnly(targetString, KeepOnlyOptions.UrlSafe));
            return sb.ToString().Replace(" ", "-").Replace("--", "-");
        }

        public enum KeepOnlyOptions
        {
            NumbersOnly,
            UrlSafe
        }

        public static string KeepOnly(this string s, KeepOnlyOptions options)
        {
            string allowed = "";
            switch (options)
            {
                case KeepOnlyOptions.NumbersOnly:
                    allowed = "1234567890";
                    break;
                case KeepOnlyOptions.UrlSafe:
                    s = s.Trim().Replace(" ", "-").Replace("--", "-").Replace("--", "-").Replace("--", "-").Replace("--", "-");
                    allowed = "1234567890_-qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM/";
                    break;
            }

            StringBuilder sb = new StringBuilder();
            foreach (char c in s)
            {
                if (allowed.Contains(c.ToString()))
                {
                    sb.Append(c.ToString());
                }
            }

            switch (options)
            {
                case KeepOnlyOptions.UrlSafe:
                    return sb.ToString().ToLowerInvariant();
                default:
                    return sb.ToString();
            }
        }

        public static string Base64Encode(this string data)
        {
            if (data == null || data == "")
                return "";
            try
            {
                byte[] encData_byte = new byte[data.Length];
                encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
                string encodedData = Convert.ToBase64String(encData_byte);
                return encodedData;
            }
            catch (Exception e)
            {
                throw new Exception("Error in base64Encode" + e.Message);
            }
        }

        public static string Base64Decode(this string data)
        {
            if (data == null || data == "")
                return "";
            try
            {
                System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
                System.Text.Decoder utf8Decode = encoder.GetDecoder();

                byte[] todecode_byte = Convert.FromBase64String(data);
                int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
                char[] decoded_char = new char[charCount];
                utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
                string result = new String(decoded_char);
                return result;
            }
            catch (Exception e)
            {
                throw new Exception("Error in base64Decode" + e.Message);
            }
        }

        public static string GetGuid()
        {
            return Guid.NewGuid().ToString().Replace("-", "_");
        }

        public static string ToJSON(this object obj)
        {
            StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
            JsonSerializer jsonSerializer = new JsonSerializer()
            {
                ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
                ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Auto,
                NullValueHandling = Newtonsoft.Json.NullValueHandling.Include
            };
            using (JsonWriter jsonWriter = new JsonTextWriter(sw))
            {
                jsonSerializer.Serialize(jsonWriter, obj);
            }
            return sw.ToString();
        }

        public static T FromJSON<T>(this string json) where T : class
        {
            return FromJSON<T>(json, null);
        }

        public static T FromJSON<T>(this string json, object targetObject) where T : class
        {
            return FromJSON<T>(json, targetObject as T);
        }

        public static T FromJSON<T>(this string json, T targetObject) where T : class
        {
            StringReader sr = new StringReader(json);
            JsonSerializer jsonSerializer = new JsonSerializer();
            using (JsonReader jsonReader = new JsonTextReader(sr))
            {
                return (T)jsonSerializer.Deserialize(jsonReader, typeof(T), targetObject);
            }
        }

        public static object FromJSON(this string json, object targetObject, Type type)
        {
            StringReader sr = new StringReader(json);
            JsonSerializer jsonSerializer = new JsonSerializer();
            using (JsonReader jsonReader = new JsonTextReader(sr))
            {
                return jsonSerializer.Deserialize(jsonReader, type, targetObject);
            }
        }

        public static bool IsNumeric(this string Criteria)
        {
            try { long lTest = Convert.ToInt64(Criteria.Trim()); }
            catch { return false; }
            return true;
        }

        public static string RepageUrl(string url, int pageNo)
        {
            Uri uri = new Uri(url);
            return RepageUrl(uri, pageNo, false);
        }

        public static string RepageUrl(Uri url, int pageNo)
        {
            return RepageUrl(url, pageNo, false);
        }

        public static string RepageUrl(string url, int pageNo, bool showAll)
        {
            Uri uri = new Uri(url);
            return RepageUrl(uri, pageNo, showAll);
        }

        public static string RepageUrl(Uri uri, int pageNo, bool showAll)
        {
            string toParse = uri.LocalPath;

            if (Regex.IsMatch(toParse, @"/page-\d*(\/||$)") || Regex.IsMatch(toParse, @"/all(\/||$)"))
            {
                if (Regex.IsMatch(toParse, @"/all(\/||$)"))
                {
                    if (showAll)
                    {
                        toParse = Regex.Replace(toParse, @"/all(\/||$)", "/all/");
                    }
                    else
                        toParse = Regex.Replace(toParse, @"/all(\/||$)", "/page-" + pageNo + "/");
                }
                else
                {
                    if (showAll)
                    {
                        toParse = Regex.Replace(toParse, @"/page-\d*(\/||$)", "/all/");
                    }
                    else
                        toParse = Regex.Replace(toParse, @"/page-\d*(\/||$)", "/page-" + pageNo + "/");
                }
            }
            else
            {
                if (toParse.Substring(toParse.Length - 1) == "/")
                    if (showAll)
                        toParse = toParse + "all/";
                    else
                        toParse = toParse + "page-" + pageNo + "/";
                else
                    if (showAll)
                        toParse = toParse + "/all/";
                    else
                        toParse = toParse + "/page-" + pageNo + "/";
            }

            return uri.ToString().Replace(uri.LocalPath, toParse);
        }

        public static string GetExceptionInfo(this Exception ex)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("--------- Exception Data ---------");
            if (ex != null)
            {
                sb.Append("Message: " + ex.Message);
                sb.Append("Exception Type: " + ex.GetType().FullName);
                sb.Append("Source: " + ex.Source);
                sb.Append("StackTrace: " + ex.StackTrace);
                sb.Append("TargetSite: " + ex.TargetSite);
            }
            return sb.ToString();
        }

        public static LiteralControl AddLiteral(this ControlCollection controls, string text)
        {
            var l = new LiteralControl(text);
            controls.Add(l);
            return l;
        }

        public static FriendlyDictionary<string, string> NameValueCollectionToDictionary(NameValueCollection collection)
        {
            FriendlyDictionary<string, string> dict = new FriendlyDictionary<string, string>();
            foreach (string key in collection.Keys)
            {
                dict.Add(key, collection[key]);
            }
            return dict;
        }

        public static string MakeLink(string txt, string target)
        {
            Regex regx = new Regex("http://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

            MatchCollection mactches = regx.Matches(txt);

            foreach (Match match in mactches)
            {
                txt = txt.Replace(match.Value, "<a href=\"" + match.Value + "\" target=\"" + target + "\">" + match.Value + "</a>");
            }

            return txt;
        }
    }
}
